package stopwatch; 

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.sql.Timestamp;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.JSpinner;
import javax.swing.KeyStroke;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import utility.Constants;
import database.Database;

public class StopwatchPanel extends JComponent implements ActionListener, ChangeListener{

	private static final long serialVersionUID = 733684742106765720L;
	private int prevActivityId;
	private Stopwatch stopwatch;
	private Alarm alarm;

	private final String PAUSE = "Pause";
	private final String RESUME = "Resume";

	private final int DEFTIME = 0;

	private final int DEF_TIME_ALARM = 0;
	private final int ALARM_INCREMENT = 15;
	private final int QUARTERS_DAY = 96;
	private final int newActivityNo = 0;

	private final JButton keyBtn = new JButton(PAUSE);

	private final SpinnerNumberModel hModel = new SpinnerNumberModel(DEFTIME,DEFTIME,Constants.HOURS_DAY,Constants.TIME_INCREMENT);
	private final SpinnerNumberModel mModel = new SpinnerNumberModel(DEFTIME,DEFTIME,Constants.MIN_DAY,Constants.TIME_INCREMENT);

	private final SpinnerNumberModel alarmIntervals = new SpinnerNumberModel(DEF_TIME_ALARM, DEF_TIME_ALARM, ALARM_INCREMENT*QUARTERS_DAY, ALARM_INCREMENT);
	private JSpinner alarmIntervalSelector;

	private final JSpinner  hhDisplay = new JSpinner(hModel);
	private final JSpinner  mmDisplay = new JSpinner(mModel);

	private final JPanel timeDisplay;
	private final JRootPane rootPane;
	private boolean alarmSet;
	private Integer AlarmtimeInterval;

	private Boolean manualModification = false;

	private final JFrame frame = new JFrame("Stopwatch");
	private final JPanel buttons = new JPanel();

	public StopwatchPanel(){
		rootPane = frame.getRootPane();
		
		timeDisplay = new JPanel();

		timeDisplay.add(hhDisplay);
		hhDisplay.setVisible(false);
		
		timeDisplay.add(mmDisplay);
		mmDisplay.setEnabled(false);
		mmDisplay.setVisible(false);

		Constants.updateActivitiesList();
		Constants.activitiesList = new JComboBox(Constants.activities);
		Constants.activitiesList.setVisible(true);
		Constants.activitiesList.addActionListener(this);
		timeDisplay.add(Constants.activitiesList);

		keyBtn.addActionListener(this);
		keyBtn.setVisible(false);
		keyBtn.registerKeyboardAction(this,KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0),WHEN_IN_FOCUSED_WINDOW);

		alarmIntervalSelector = new JSpinner(alarmIntervals);
		alarmIntervalSelector.setVisible(true);

		frame.getContentPane().add(buttons, BorderLayout.SOUTH);
		frame.getContentPane().add(timeDisplay, BorderLayout.NORTH);

		frame.setFocusable(true);
		frame.setVisible(true);
		frame.requestFocus();
		frame.setLocationRelativeTo(null);
		frame.pack();
	}

	private void runningInterface(final boolean star){
		keyBtn.setVisible(true);
		keyBtn.setText(PAUSE);
		buttons.add(keyBtn);
		grabFocus(keyBtn);
		hhDisplay.setVisible(true);
		hhDisplay.setEnabled(false);
		mmDisplay.setVisible(true);
		mmDisplay.setEnabled(false);
		
		hhDisplay.removeChangeListener(this);
		mmDisplay.removeChangeListener(this);

		alarmIntervalSelector.addChangeListener(this);
		buttons.add(alarmIntervalSelector);

		Constants.activitiesList.setEditable(true);
		frame.requestFocus();
		frame.pack();
		frame.setState(Frame.ICONIFIED);
	}

	private void pauseInterface(){

		grabFocus(keyBtn);
		keyBtn.setVisible(true);
		keyBtn.setText(RESUME);

		hhDisplay.setVisible(true);
		mmDisplay.setVisible(true);
		hhDisplay.addChangeListener(this);
		mmDisplay.addChangeListener(this);

		if ((Integer) mmDisplay.getValue() > 0 || (Integer) hhDisplay.getValue() > 0 ){
			hhDisplay.setEnabled(true);		
			mmDisplay.setEnabled(true);
		}

		alarmIntervalSelector.setVisible(true);

		Constants.activitiesList.setVisible(true);
		Constants.activitiesList.setEditable(true);
		frame.validate();
		frame.pack();
	}

	void updateDisplayedTime(final int Helapsed,final int Melapsed){
		if (Helapsed < 0 || Melapsed < 0) throw new RuntimeException("Given following hh:mm " + Helapsed + " " + Melapsed + ".\n");
			hhDisplay.setValue(Helapsed);
			mmDisplay.setValue(Melapsed);
	}
	
	private void grabFocus(final JButton btn){
		rootPane.setDefaultButton(btn);
		btn.grabFocus();
	}

	private void startActivity(final boolean lap){
		final int selectedActivityId = Constants.getSelectedActivityId();
		if (selectedActivityId == newActivityNo) Constants.addActivity2List(Constants.activitiesList.getSelectedItem().toString());
		assert(stopwatch.isSessionPaused());
		if (manualModification){
			manualModification = false;
			stopwatch = new Stopwatch(this);
		}
		stopwatch.startOrPauseOrResume(prevActivityId = Constants.getSelectedActivityId(), lap);
		runningInterface(!lap);
	}

	private void pauseActivity(final int activityId){
		stopwatch.startOrPauseOrResume(activityId ,false); //doesn't matter if lap
		pauseInterface();
	}

	public void actionPerformed(ActionEvent stopwatchEvent){
		if (stopwatchEvent.getSource() == keyBtn){
			if (!stopwatch.isSessionPaused()) pauseActivity(prevActivityId);
			else startActivity(false);
		}
		else
			if (stopwatchEvent.getSource() == Constants.activitiesList){
				if (keyBtn.isVisible() && keyBtn.getText().equals(PAUSE)) keyBtn.doClick(); 
				stopwatch = new Stopwatch(this);
				System.gc();
				startActivity(false);
			}
	
		frame.requestFocus();
		frame.pack();
	}

	public void stateChanged(ChangeEvent e){
		if (e.getSource() == alarmIntervalSelector){
			if (!alarmSet)
				if ((AlarmtimeInterval = (Integer) alarmIntervalSelector.getValue()) != 0){
					alarm = new Alarm();
					alarm.set(AlarmtimeInterval);  
					alarmSet = true;
				}
				else{
					if ((AlarmtimeInterval = (Integer) alarmIntervalSelector.getValue()) != 0){
						alarm.cancel(); 
						alarm.set(AlarmtimeInterval); 
						alarmSet = true;
					} 
					else{
						alarm.cancel(); 
						alarmSet = false;
					}
				}
		}
		else{
			if ((e.getSource() == mmDisplay || e.getSource() == hhDisplay) && mmDisplay.isEnabled()){ 
				final Timestamp startTime = Database.getLastFirstDate(prevActivityId);
				long newEndTime = startTime.getTime();
				
				if (e.getSource() == mmDisplay){					
					final int mm = (Integer) mmDisplay.getValue();
					newEndTime += mm*Constants.MM_MILLIS;
				}
				else{
					final int hh = (Integer) hhDisplay.getValue();
					newEndTime += hh*Constants.HH_MILLIS;
				}
				Database.modifyEndTimeOf(prevActivityId,newEndTime);
				manualModification = true;
			}
		}
	}
}
